# -*- coding: utf-8 -*-
import json
import os

import javalang

from compare_module.base_code_parse import parse_java_code, is_custom_object
from compare_module.diff_type import DiffType
from compare_module.export_csv import append_export_data


# 比较两份Java代码的接口参数
def compare_controller_params(controllerA, controllerB,export_data):


    tree1 = parse_java_code(controllerA)
    tree2 = parse_java_code(controllerB)

    old_methods = extract_controller_params(tree1)
    new_methods = extract_controller_params(tree2)

    removed_methods = [method_name for method_name in old_methods.keys() if method_name not in new_methods.keys()]
    before_diff = False
    #检查controller中有没有删除的方法
    if removed_methods:
        before_diff = True
        append_export_data(export_data,controllerB,'',DiffType.METHOD,json.dumps(removed_methods))

    for old_method_name,old_method in old_methods.items():
        for new_method_name,new_method in new_methods.items():
            if old_method_name == new_method_name:
                # url不相等
                if old_method['url'] != new_method['url']:
                    append_export_data(export_data,controllerB,new_method_name,DiffType.URL,old_method['url']+":"+new_method['url'])
                # 请求方式不同
                if old_method['request_type'] != new_method['request_type']:
                    append_export_data(export_data,controllerB,new_method_name,DiffType.REQUEST_TYPE,old_method['request_type']+":"+new_method['request_type'])
                removed_param_names = [param for param in old_method['param_names'] if param not in new_method['param_names']]
                # 返回参数名不同
                if old_method['return_type_name'] != new_method['return_type_name']:
                    append_export_data(export_data,controllerB,new_method_name,DiffType.RETURN_TYPE_NAME,old_method['return_type_name']+":"+new_method['return_type_name'])
                #检查方法参数中有没有删除的参数名
                if removed_param_names:
                    # controllerA,controllerB,removed_params
                    before_diff = True
                    append_export_data(export_data,controllerB,new_method_name,DiffType.METHOD_PARAM_NAMESE,json.dumps(removed_param_names))
                removed_params = [param for param in old_method['params'] if
                                       param not in new_method['params']]
                #参数不同
                if removed_params:
                    before_diff = True
                    # controllerA,controllerB,removed_params
                    removed_param_names = [param.type.name + " "+ param.name for param in removed_params]
                    append_export_data(export_data, controllerB, new_method_name, DiffType.METHOD_PARAMS,json.dumps(removed_param_names))
                #比较参数名上添加的注解
                for old_param in old_method['params']:
                    for new_param in new_method['params']:
                        old_param_annotation_names = []
                        new_param_annotation_names = []
                        for annotation in old_param.annotations:
                            old_param_annotation_names.append(annotation.name)
                        for annotation in new_param.annotations:
                            new_param_annotation_names.append(annotation.name)
                        if sorted(old_param_annotation_names) != sorted(new_param_annotation_names):
                            before_diff = True
                            append_export_data(export_data, controllerB, new_method_name, DiffType.METHOD_PARAM_ANNOTATIONS,
                                               json.dumps(old_param_annotation_names)+":"+json.dumps(new_param_annotation_names))

                # 参数列表中的自定义包装对象比较,如果方法和参数例表都不同，那么就不继续向下比较对象成员
                if before_diff:
                    return None

                type_names = set([])
                for param in old_method['params']:
                    type_name = param.type.name
                    if is_custom_object(type_name):
                        # 自定义的包装对象，就进一步比较它的类成员
                        type_names.add(type_name)
                return type_names,controllerA,controllerB,new_method_name


# 提取Controller接口参数
def extract_controller_params(tree):

    methods = {}
    for path, node in tree:
        if isinstance(node, javalang.tree.MethodDeclaration):
           params = []
           param_names = []
           url = ''
           request_type = ''
           for annotation in node.annotations:
               if 'RequestMapping' == annotation.name or 'GetMapping' == annotation.name or 'PostMapping' == annotation.name\
                       or 'DeleteMapping' == annotation.name or 'PutMapping' == annotation.name:
                   request_type == annotation.name
                   if hasattr(annotation,'children') and len(annotation.children)>1 and hasattr(annotation.children[1],'value'):
                        url = annotation.children[1].value
           method_annotation_names = [annotation.name for annotation in node.annotations]
           method_name = node.name
           return_type_name = node.return_type.name
           if 'RequestMapping' in method_annotation_names or 'GetMapping' in method_annotation_names or 'PostMapping' in method_annotation_names:
                for parameter in node.parameters:
                    params.append(parameter)
                    param_names.append(parameter.type.name)
                    methods[method_name] = {
                        'url':url,
                        'request_type':request_type,
                        'params':params,
                        'param_names':param_names,
                        'return_type_name':return_type_name
                    }
    return methods


#找出一个目录下的所有controller
def find_controller_files(root_dir):
    controller_files = []
    all_files = []
    for dirpath, dirnames, filenames in os.walk(root_dir):
        for filename in filenames:
            all_files.append(os.path.join(dirpath,filename))
            if filename.endswith("Controller.java"):
                controller_files.append(os.path.join(dirpath, filename))
    return controller_files,all_files